/*
	Copyright (c) 2010 <copyright holders>
	This source code is licensed under The MIT License,
	see the file LICENSE for details.
	*/

#ifndef SQUAD_HPP
#define SQUAD_HPP

#include <boost/scoped_ptr.hpp>
#include <list>

#include "ForwardDecls.hpp"
#include "gen/auxmsg.pb.h"
#include "Landscape.hpp"
#include "Unit.hpp"

namespace spelprog2 {
namespace sim {

/** Class that encapsulates the data and behaviour of a squad's order.
    */
struct SquadOrder {
    typedef boost::shared_ptr<SquadOrder> Pointer;
protected:
    Squad & squad_;
public:
    SquadOrder(Squad & squad) : squad_(squad) { }
    virtual ~SquadOrder() { }

    /** Called each frame to update the squad's order.
        @return If false, an inherting class' simUpdate(Int) must not continue after this was called.
        */
    virtual bool simUpdate(Int sequenceNbr) = 0;

    //! Is this order finished and can be popped from order list?
    virtual bool finished() = 0;
};

struct Squad : public boost::enable_shared_from_this<Squad> {
    typedef boost::shared_ptr<Squad>        Pointer;
    typedef boost::shared_ptr<const Squad>  ConstPointer;
    typedef boost::weak_ptr<Squad>          WeakPointer;
    typedef boost::weak_ptr<const Squad>    WeakConstPointer;
private:
    HIDE_EVIL_CONSTRUCTORS(Squad);

    typedef std::list<Unit::Pointer> Units;
    typedef std::list<SquadOrder::Pointer> Orders;

    Int id_;
    Int playerId_;
    World & world_;
    Units units_;
    auxmsg::Formation formation_;
    Int formationWidth_;
    Int typicalRadius_;
    Int facing_;
    Orders orders_;

    Squad(Int playerId, Int id, World & world, const Int2d & position,
        Int numUnits, const std::string & squadType);

    Int2d calcUnitPosition(Int index, Int n) const;
    Int calcUnitFacing(Int index, Int n) const;
public:
    static Pointer create(Int playerId, Int id, World & world, const Int2d & position,
        Int numUnits, const std::string & squadType);
    ~Squad() { }

    // Actions ================================================================

    //! Simulation frame update.
    void simUpdate(Int sequenceNbr);
    //! Queues a SquadOrder.
    void queueOrder(SquadOrder::Pointer order, bool overwrite);
    //! Sets the team of the squad's units. Squads on different teams will attack each others.
    void setTeam(Int team);

    // Methods for SquadOrder use =============================================

    //! Set's the target facing of this squad. Intended for SquadOrders.
    void setFacing(Int newFacing);
    //! Sets the position the squad will attempt to move to. Intended for SquadOrders.
	void setTarget(const Int2d & position);
    //! Causes all ready units to be given new targets. Intended for SquadOrders.
    void retargetSquad(Squad & other);
    //! Changes the formation of this squad. Intended for SquadOrders.
    void setFormation(auxmsg::Formation newFormation);

    // Queries ================================================================

    //! Gets the optimal path for this squad, taking the formation into account.
    bool getOptimalPath(const Int2d &target, LandscapeSpatials::PointList &list);
    //! Gets the bounding diameter of the squads target formation.
    Int getSize(){return Int(5000);}
    //! Returns squad ID.
    Int getId() const { return id_; }
    //! Returns the player ID.
    Int getPlayerId() const { return playerId_; }
    //Gets the "position" of the squad (an average of all squad members').
    Int2d getPosition() const;
    //! Counts the number of alive units.
    Int countAliveUnits() const;
    //! Counts the number of active units (neither fleeing nor dead).
    Int countActiveUnits() const;
    //! Counts the number of ready (to accept orders) units. (Idle or moving.)
    Int countReadyUnits() const;
    //! Are all active units idle?
    bool allActiveUnitsIdle() const;
    //! If this squad has no active units it is defeated.
    bool isDefeated() const;
    //! Returns a valid Squad::Pointer if this is squad is being attacked by at least one squad (the pointer is to such an attacker).
    Squad::Pointer beingAttackedBySquad();
};

/** Simply a filler command that does nothing except making squad react to enemy attacks.
    */
struct IdleSquadOrder : public SquadOrder {
    IdleSquadOrder(Squad & squad) : SquadOrder(squad) { }

    virtual bool simUpdate(Int sequenceNbr);
    virtual bool finished() { return true; }
};

/** Order that in addition to IdleSquad behaviour makes units regroup at current position
    */
struct StopSquadOrder : public IdleSquadOrder {
    StopSquadOrder(Squad & squad);
};

/** Orders a squad to move to a position on the map. A path is calculated once and then followed.
    */
class MoveSquadOrder : public IdleSquadOrder {
    Int2d target_;
    LandscapeSpatials::PointList path_;

    enum {
        FACE_DIRECTION,
        NEXT_WAYPOINT,
        FINISHED
    } state_;

    void toNextWaypoint();
public:
    MoveSquadOrder(Squad & squad, const Int2d & target);
    virtual bool simUpdate(Int sequenceNbr);
    virtual bool finished();
};

/** An extension of MoveSquadOrder that moves towards a target squad rather than a fixed point.
    The path is updated at regular intervals.
    */
class AttackSquadOrder : public MoveSquadOrder {
    Squad::WeakPointer target_;
public:
    AttackSquadOrder(Squad & squad, Squad & target);
    //TODO: override so that path is recalculated at every waypoint
};

/** Simply changes the formation of a squad to a different one.
    */
class ChangeFormationSquadOrder : public IdleSquadOrder {
    auxmsg::Formation target_;
public:
    ChangeFormationSquadOrder(Squad & squad, auxmsg::Formation target);
    virtual bool simUpdate(Int sequenceNbr);
    virtual bool finished();
};

/** Orders the squad's units to attack the units of another squad. This continues until all the opposing units are
    out of range. (Or something else occurred like either squad being completely annihilated or routed.)
    */
class ChargeSquadOrder : public IdleSquadOrder {
    Squad::WeakPointer target_;
    bool finished_; //cahced result because calculation is quite expensive
public:
    ChargeSquadOrder(Squad & squad, Squad & target);
    virtual bool simUpdate(Int sequenceNbr);
    virtual bool finished();
};

}; //sim
}; //spelprog2

#endif //SQUAD_HPP
